/*
 *                 Sun Public License Notice
 *
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 *
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
 * Microsystems, Inc. All Rights Reserved.
 */


package org.medbeans.modules.search;


import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.Image;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyVetoException;
import java.util.ArrayList;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.ObjectStreamException;
import javax.swing.ButtonGroup;
import javax.swing.border.EtchedBorder;
import javax.swing.border.TitledBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import org.openide.actions.HelpAction;
import org.openide.awt.SplittedPanel;
import org.openide.explorer.ExplorerManager;
import org.openide.explorer.ExplorerPanel;
import org.openide.explorer.view.BeanTreeView;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.util.actions.SystemAction;
import org.openide.util.HelpCtx;
import org.openide.util.NbBundle;
import org.openide.util.Utilities;
import org.openide.windows.TopComponent;
import org.openide.windows.Workspace;
import org.openidex.search.SearchGroup;
import org.openidex.search.SearchType;


/**
 * Top component which displays search results in explorer like manner.
 * 
 * @author Petr Kuzel, Jiri Mzourek, Peter Zavadsky
 * @see org.openide.windows.TopComponent
 */
public class ResultViewTopComponent extends TopComponent implements ChangeListener, ExplorerManager.Provider {
    
    /** Result data model. */
    private ResultModel resultModel = null;
    
    /** ExplorerManager.Provider. */
    private ExplorerPanel explorerPanel;
    
    /** Detail panel showing info about found selected object. */
    private DetailsPanel detailsPanel;

    /** Listener to found nodes. */
    private PropertyChangeListener propertyListener;
    
    
    /** Creates new form ResultViewTopComponent */
    public ResultViewTopComponent(ResultModel resultModel) {
        initComponents();
        
        ButtonGroup buttonGroup = new ButtonGroup();
        buttonGroup.add(sortButton);
        buttonGroup.add(unsortButton);
        
        explorerPanel = new ResultExplorerPanel();
        explorerPanel.setLayout(new BorderLayout());

        detailsPanel = new DetailsPanel(this);

        propertyListener = new PropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent evt) {
                if (ExplorerManager.PROP_SELECTED_NODES.equals(evt.getPropertyName())) {

                    Node[] nodes = explorerPanel.getExplorerManager().getSelectedNodes();
                    setActivatedNodes(nodes);

                    if(nodes != null && nodes.length == 1) {
                        detailsPanel.showInfo(nodes[0]);
                        detailsPanel.setBorder(new TitledBorder(
                            NbBundle.getBundle(ResultViewTopComponent.class).getString("TEXT_LABEL_DETAILS") + " " + nodes[0].getName())); // NOI18N
                        
                        showDetails(nodes[0]);
                    } else {
                        detailsPanel.showInfo(null);
                        showDetails(null);
                    }
                }
            }
        };
        
        explorerPanel.getExplorerManager().addPropertyChangeListener(propertyListener);
        
        BeanTreeView treeView =  new BeanTreeView();
        treeView.getAccessibleContext().setAccessibleDescription(NbBundle.getBundle(ResultViewTopComponent.class).getString("ACS_TREEVIEW"));        
        
        treeView.setBorder(new EtchedBorder());
        explorerPanel.add(treeView, BorderLayout.CENTER);

        resultLabel.setLabelFor(treeView);
        
        splittedPanel.setSwapPanesEnabled(false);
        splittedPanel.setSplitTypeChangeEnabled(false);
        splittedPanel.add(explorerPanel, SplittedPanel.ADD_FIRST);
        splittedPanel.add(detailsPanel, SplittedPanel.ADD_SECOND);
        
        setName(NbBundle.getBundle(ResultViewTopComponent.class).getString("TEXT_TITLE_SEARCH_RESULTS")); // NOI18N
        
        // Set model.
        setModel(resultModel);
        
        initAccessibility();
    }

    private void initAccessibility() {
        sortButton.getAccessibleContext().setAccessibleDescription(NbBundle.getBundle(ResultViewTopComponent.class).getString("ACS_TEXT_BUTTON_SORT"));        
        stopButton.getAccessibleContext().setAccessibleDescription(NbBundle.getBundle(ResultViewTopComponent.class).getString("ACS_TEXT_BUTTON_STOP"));        
        closeButton.getAccessibleContext().setAccessibleDescription(NbBundle.getBundle(ResultViewTopComponent.class).getString("ACS_TEXT_BUTTON_CANCEL"));
        customizeButton.getAccessibleContext().setAccessibleDescription(NbBundle.getBundle(ResultViewTopComponent.class).getString("ACS_TEXT_BUTTON_CUSTOMIZE"));                
        outputButton.getAccessibleContext().setAccessibleDescription(NbBundle.getBundle(ResultViewTopComponent.class).getString("ACS_TEXT_BUTTON_FILL"));
        unsortButton.getAccessibleContext().setAccessibleDescription(NbBundle.getBundle(ResultViewTopComponent.class).getString("ACS_TEXT_BUTTON_UNSORT"));
        helpButton.getAccessibleContext().setAccessibleDescription(NbBundle.getBundle(ResultViewTopComponent.class).getString("TEXT_BUTTON_HELP"));
    }       
    
    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    private void initComponents() {//GEN-BEGIN:initComponents
        java.awt.GridBagConstraints gridBagConstraints;

        resultLabel = new javax.swing.JLabel();
        sortButton = new javax.swing.JRadioButton();
        unsortButton = new javax.swing.JRadioButton();
        buttonPanel = new javax.swing.JPanel();
        stopButton = new javax.swing.JButton();
        outputButton = new javax.swing.JButton();
        customizeButton = new javax.swing.JButton();
        closeButton = new javax.swing.JButton();
        helpButton = new javax.swing.JButton();
        splittedPanel = new org.openide.awt.SplittedPanel();

        setLayout(new java.awt.GridBagLayout());

        resultLabel.setText(NbBundle.getBundle(ResultViewTopComponent.class).getString("TEXT_LABEL_SEARCH_RESULTS"));
        resultLabel.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT);
        resultLabel.setDisplayedMnemonic(NbBundle.getBundle(ResultViewTopComponent.class).getString("TEXT_LABEL_SEARCH_RESULTS_MNEM").charAt(0));
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridwidth = 3;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.insets = new java.awt.Insets(12, 12, 0, 11);
        add(resultLabel, gridBagConstraints);

        sortButton.setMnemonic(NbBundle.getBundle(ResultViewTopComponent.class).getString("TEXT_BUTTON_SORT_MNEM").charAt(0));
        sortButton.setText(NbBundle.getBundle(ResultViewTopComponent.class).getString("TEXT_BUTTON_SORT"));
        sortButton.setEnabled(false);
        sortButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                sortButtonActionPerformed(evt);
            }
        });

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.insets = new java.awt.Insets(11, 12, 0, 0);
        add(sortButton, gridBagConstraints);

        unsortButton.setMnemonic(NbBundle.getBundle(ResultViewTopComponent.class).getString("TEXT_BUTTON_UNSORT_MNEM").charAt(0));
        unsortButton.setSelected(true);
        unsortButton.setText(NbBundle.getBundle(ResultViewTopComponent.class).getString("TEXT_BUTTON_UNSORT"));
        unsortButton.setEnabled(false);
        unsortButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                sortButtonActionPerformed(evt);
            }
        });

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.gridwidth = 2;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.insets = new java.awt.Insets(11, 11, 0, 11);
        add(unsortButton, gridBagConstraints);

        buttonPanel.setLayout(new java.awt.GridLayout(1, 4, 5, 0));

        stopButton.setMnemonic(NbBundle.getBundle(ResultViewTopComponent.class).getString("TEXT_BUTTON_STOP_MNEM").charAt(0));
        stopButton.setText(NbBundle.getBundle(ResultViewTopComponent.class).getString("TEXT_BUTTON_STOP"));
        stopButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                stopButtonActionPerformed(evt);
            }
        });

        buttonPanel.add(stopButton);

        outputButton.setMnemonic(NbBundle.getBundle(ResultViewTopComponent.class).getString("TEXT_BUTTON_FILL_MNEM").charAt(0));
        outputButton.setText(NbBundle.getBundle(ResultViewTopComponent.class).getString("TEXT_BUTTON_FILL"));
        outputButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                outputButtonActionPerformed(evt);
            }
        });

        buttonPanel.add(outputButton);

        customizeButton.setMnemonic(NbBundle.getBundle(ResultViewTopComponent.class).getString("TEXT_BUTTON_CUSTOMIZE_MNEM").charAt(0));
        customizeButton.setText(NbBundle.getBundle(ResultViewTopComponent.class).getString("TEXT_BUTTON_CUSTOMIZE"));
        customizeButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                customizeButtonActionPerformed(evt);
            }
        });

        buttonPanel.add(customizeButton);

        closeButton.setMnemonic((NbBundle.getBundle(ResultViewTopComponent.class).getString("TEXT_BUTTON_CANCEL")).charAt(0));
        closeButton.setText(NbBundle.getBundle(ResultViewTopComponent.class).getString("TEXT_BUTTON_CANCEL"));
        closeButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                closeButtonActionPerformed(evt);
            }
        });

        buttonPanel.add(closeButton);

        helpButton.setMnemonic(NbBundle.getBundle(ResultViewTopComponent.class).getString("TEXT_BUTTON_HELP_MNEM").charAt(0));
        helpButton.setText(NbBundle.getBundle(ResultViewTopComponent.class).getString("TEXT_BUTTON_HELP"));
        helpButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                helpButtonActionPerformed(evt);
            }
        });

        buttonPanel.add(helpButton);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 3;
        gridBagConstraints.gridwidth = 3;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
        gridBagConstraints.insets = new java.awt.Insets(11, 11, 11, 11);
        add(buttonPanel, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.gridwidth = 3;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.weighty = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(0, 12, 0, 11);
        add(splittedPanel, gridBagConstraints);

    }//GEN-END:initComponents

    private void helpButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_helpButtonActionPerformed
        // Add your handling code here:
        SystemAction.get(HelpAction.class).actionPerformed(new ActionEvent(this, 0, null));
    }//GEN-LAST:event_helpButtonActionPerformed

    
    private void closeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_closeButtonActionPerformed
        close();
    }//GEN-LAST:event_closeButtonActionPerformed
    
    private void outputButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_outputButtonActionPerformed
        resultModel.fillOutput();
    }//GEN-LAST:event_outputButtonActionPerformed
    
    private void sortButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_sortButtonActionPerformed
        ExplorerManager explorerManager = null;
        Node [] selectedNodes = null;
        if ((explorerManager = explorerPanel.getExplorerManager()) != null){
            selectedNodes = explorerManager.getSelectedNodes();
        }
        
        boolean sort = sortButton.isSelected();
        sortNodes(sort);

        if(selectedNodes!=null) {        
            try {
                explorerManager.setSelectedNodes(selectedNodes);
            } catch(PropertyVetoException pve) {
                if(Boolean.getBoolean("netbeans.debug.exceptions"))
                    pve.printStackTrace();
                // OK it was vetoed.
            }
        }
    }//GEN-LAST:event_sortButtonActionPerformed
    
    private void customizeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_customizeButtonActionPerformed
        customizeCriteria();
    }//GEN-LAST:event_customizeButtonActionPerformed
    
    private void stopButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_stopButtonActionPerformed
        resultModel.stop();
    }//GEN-LAST:event_stopButtonActionPerformed
    
    
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JRadioButton sortButton;
    private javax.swing.JPanel buttonPanel;
    private javax.swing.JButton stopButton;
    private javax.swing.JButton closeButton;
    private javax.swing.JButton customizeButton;
    private org.openide.awt.SplittedPanel splittedPanel;
    private javax.swing.JButton outputButton;
    private javax.swing.JRadioButton unsortButton;
    private javax.swing.JButton helpButton;
    private javax.swing.JLabel resultLabel;
    // End of variables declaration//GEN-END:variables
    

    /** Set new model. */
    public void setModel(ResultModel resultModel) {
        if (this.resultModel != null) {
            this.resultModel.removeChangeListener(this);
        }
        
        this.resultModel = resultModel;
        Node root = resultModel.getRoot();

        if(resultModel.isSorted())
            sortButton.setSelected(true);
        else
            unsortButton.setSelected(true);
        
        explorerPanel.getExplorerManager().setRootContext(root);
        resultModel.addChangeListener(this);
        initButtons();
    }
    
    /** Delegates activation to explorer panel. */
    protected void componentActivated() {
        ((ResultExplorerPanel)explorerPanel).componentActivated();
    }
    
    /** Delegates deactivation to explorer panel. */
    protected void componentDeactivated() {
        ((ResultExplorerPanel)explorerPanel).componentDeactivated();
    }
    
    /** Set visibility of buttons & others... */
    private void initButtons() {
        stopButton.setEnabled(!resultModel.isDone());
        sortButton.setEnabled(resultModel.isDone());
        unsortButton.setEnabled(resultModel.isDone());
        
        detailsPanel.showInfo(null);
    }
    
    /** Sorts nodes. */
    public void sortNodes(boolean sort) {
        Node root = resultModel.sortNodes(sort);
        explorerPanel.getExplorerManager().setRootContext(root);
        initButtons();
    }
    
    
    /** Gets icon. Overrides superclass method.
     * @return search icon. */
    public Image getIcon() {
        return Utilities.loadImage("/org/netbeans/modules/search/res/find.gif"); // NOI18N
    }
    
    /** Gets explrer manager.
     * @return <code>ExplorerPanel.Provider</code> */
    public ExplorerManager getExplorerManager() {
        return explorerPanel.getExplorerManager();
    }
    
    /** Stops search before closing and hides details. Overrides superclass method. */
    public boolean canClose(Workspace workspace, boolean last) {
        if(resultModel != null) {
            resultModel.stop();
            resultModel.removeChangeListener(this);
        }
        
        return true;
    }
    
    /** (Re)open the dialog window for entering (new) search criteria. */
    private void customizeCriteria() {
        SearchPanel searchPanel = new SearchPanel(resultModel.getEnabledSearchTypes(), true);
        
        Node[] oldRoots = resultModel.getSearchGroup().getSearchRoots();
        
        searchPanel.showDialog();
        
        if(searchPanel.getReturnStatus() == SearchPanel.RET_OK) {
            //stop previous search
            resultModel.stop();
            
            // Start a new search.
            SearchEngine searchEngine = new SearchEngine();
            
            SearchGroup[] groups = SearchGroup.createSearchGroups(searchPanel.getCustomizedSearchTypes());

            SearchGroup searchGroup = null;

            if(groups.length > 0) {
                // PENDING Here should be solved cases when more groups were created,
                // if not only intersection result is necessary etc.
                searchGroup = groups[0];
            }
            
            ResultModel newResultModel = new ResultModel(resultModel.getEnabledSearchTypes(), searchGroup);

            SearchTask task = searchEngine.search(
                //criteriaModel.getNodes(),
                oldRoots,
                searchGroup,
                newResultModel);
            
            newResultModel.setTask(task);
            
            setModel(newResultModel);
            initButtons();
        }
    }
    
    /** Show search details (of the selected node) in the detail list. */
    public void showDetails(Node node) {
        Children children;
        
        if(node == null) {
            children = Children.LEAF;
        } else {
        
            children = new Children.Array();

            ArrayList listData = new ArrayList(20);

            SearchType[] searchTypes = resultModel.getSearchGroup().getSearchTypes();
            
            for(int i = 0; i < searchTypes.length; i++) {
                Node[] detailNodes = searchTypes[i].getDetails(node);

                if(detailNodes != null)
                    children.add(detailNodes);
            }
        }
        
        detailsPanel.setDetailsRoot(new AbstractNode(children));
    }
    
    /** Respond to result model state change. */
    public void stateChanged(ChangeEvent evt) {
        if(evt.getSource() == resultModel) {
            if(resultModel.isDone()) {
                initButtons();
            }
        }
    }

    /** Gets help context for this top component. Overrides superclass method. */
    public HelpCtx getHelpCtx() {
        return new HelpCtx(ResultViewTopComponent.class);
    }
    
    /** Overrides superclass method to avoid serialization. */
    public void writeExternal(ObjectOutput out) throws IOException {}
    
    /** Overrides superclass method to avoid serialization. */
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {}
    
    /** Overrides superclass method to avoid serialization. */
    public Object writeReplace() throws ObjectStreamException {
        return null;
    }
    
    
    /** Explorer panel in search result window. */
    private static class ResultExplorerPanel extends ExplorerPanel {
        
        /** Extends scope of superclass method. */
        public void componentActivated() {
            super.componentActivated();
        }
        
        /** Extends scope of superclass method. */
        public void componentDeactivated() {
            super.componentDeactivated();
        }
        
    } // End of class ResultExploererPanel.
    
}
